package com.calligraphy.user.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author hy
 * @Date 2018/12/28 14:29
 **/
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate redisTemplate;

    private RedisSerializer<Object> getKeySerializer() {
        return (RedisSerializer<Object>) redisTemplate.getKeySerializer();
    }

    private byte[] getKeyBytes(Object key) {
        return getKeySerializer().serialize(key);
    }

    private RedisSerializer<Object> getHashValueSerializer() {
        return (RedisSerializer<Object>) redisTemplate.getHashValueSerializer();
    }

    private RedisSerializer<Object> getHashKeySerializer() {
        return (RedisSerializer<Object>) redisTemplate.getHashKeySerializer();
    }

    private byte[] getHashValueBytes(Object value) {
        return getHashValueSerializer().serialize(value);
    }

    private byte[] getHashKeyBytes(Object key) {
        return getHashKeySerializer().serialize(key);
    }

    private <T> T deserializeHashValue(byte[] bytes) {
        return (T) getHashValueSerializer().deserialize(bytes);
    }


    public boolean hSet(final Object key, final Object field, final Object value) {
        boolean result = (boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hSet(getKeyBytes(key), getHashKeyBytes(field), getHashValueBytes(value));
            }
        });
        return result;
    }

    public <T> T hGet(final Object key, final Object field) throws DataAccessException {
        byte[] result = (byte[]) redisTemplate.execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.hGet(getKeyBytes(key), getHashKeyBytes(field));
            }
        });
        if (result == null) {
            return null;
        }
        return deserializeHashValue(result);
    }

    /**
     * 用于删除哈希表 key 中的一个或多个指定字段，不存在的字段将被忽略
     *
     * @param key   哈希表对应的键
     * @param filed 哈希表中要删除的键
     * @return
     */
    public Long hdel(final Object key, final Object... filed) {
        if (key == null) {
            return 0L;
        }
        final byte[] keys = getKeyBytes(key);
        if (keys == null || keys.length == 0) {
            return 0L;
        }
        Long delNum = (Long) redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                List<byte[]> fieldList = new ArrayList<byte[]>();
                for (Object f : filed) {
                    if (f != null) {
                        byte[] hfs = getHashKeyBytes(f);
                        if (hfs != null && hfs.length > 0) {
                            fieldList.add(hfs);
                        }
                    }
                }

                if (fieldList.size() == 0) {
                    return 0L;
                }

                byte[][] bytes = new byte[0][];
                return connection.hDel(keys, fieldList.toArray(bytes));
            }
        });
        return delNum;
    }

    public Map<Object, Object> hashGetAll(final String key) throws DataAccessException {
        return redisTemplate.opsForHash().entries(key);
    }
}
